#include "How2GoQuery.h"
#include <Binder.h>
#include "CharUtils.h"
#include <ezgzip.h>
#include <FunTrace.h>
#include <eikenv.h>
#include <eikappui.h>
#include <eikapp.h>
#include <SingaporeBusDocument.h>

const TInt DndTimedOut = -5120;


const TInt kHow2GoTimeout = 45*1000000;

CHow2GoQuery::COutput::CItinerary::~CItinerary()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iItems.Close();
	}

CHow2GoQuery::COutput::COutput() : iHow2GoAnswer(NULL)
	{
	
	}
CHow2GoQuery::COutput::~COutput()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	for (TInt i = 0 ; i < iItineraries.Count() ; ++i)
		delete iItineraries[i];
	iItineraries.Close();
	delete iHow2GoAnswer;
	}

static TPtrC8 IrisExtractField(const TDesC8& aPrefix, const TDesC8& aSuffix, TPtrC8& aBuf)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	TPtrC8 ret;
	TInt begfield = aBuf.Find(aPrefix);
	if(begfield != KErrNotFound)
		{
		TInt endfield = aBuf.Mid(begfield + aPrefix.Length()).Find(aSuffix);
		if(endfield != KErrNotFound)
			{
			ret.Set( aBuf.Mid(begfield + aPrefix.Length(), endfield) );
			aBuf.Set( aBuf.Mid( begfield + aPrefix.Length() + endfield + aSuffix.Length() ) );
			return ret;
			}
		}
	aBuf.Set( aBuf.Mid( aBuf.Length() ) );
	return ret;
	}



CHow2GoQuery::CHow2GoQuery()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iHow2GoTimeoutTimer.CreateLocal();	
	}

CHow2GoQuery::~CHow2GoQuery()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	Cancel();
	delete iHow2GoTimeoutTarget.Unmanage();
	iHow2GoTimeoutTimer.Close();
	iHTTPSession.Close();
	}

CHow2GoQuery* CHow2GoQuery::NewL()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
CHow2GoQuery* self = CHow2GoQuery::NewLC();
	CleanupStack::Pop( self );
	return self;
	}

CHow2GoQuery* CHow2GoQuery::NewLC()
	{
TFunTrace funTrace(__PRETTY_FUNCTION__);
	CHow2GoQuery* self = new ( ELeave ) CHow2GoQuery();
	CleanupStack::PushL( self );
	self->ConstructL();
	return self;
	}

void CHow2GoQuery::ConstructL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iHow2GoTimeoutTarget = new (ELeave) scalp::CActiveTarget(
			scalp::MakeFunctionL ( scalp::Bind(&RTimer::Cancel, &iHow2GoTimeoutTimer) )
			);
	iHow2GoTimeoutTarget->AppendRunL(
			scalp::MakeFunctionL ( scalp::Bind(&CHow2GoQuery::How2GoTransactionTimeoutL, this) )
			);
	iHow2GoTransactionTarget = CHTTPTransactionTarget::NewL();
	iHow2GoTransactionTarget->SetRun( scalp::MakeFunctionL( scalp::Bind(&CHow2GoQuery::ReceivedHow2GoAnswerL, this, scalp::Free1, scalp::Free2, scalp::Free3, scalp::Free4) ) );
	iHow2GoTransactionTarget->SetRunError( scalp::MakeFunctionL( scalp::Bind(&CHow2GoQuery::How2GoTransactionError, this, scalp::Free1, scalp::Free2) ) );
	iHow2GoTransactionSource = new (ELeave) CHTTPTransactionSource;
	iDeflateDecompressor = CDeflateDecompressor::NewL();
	}

void CHow2GoQuery::How2GoTransactionTimeoutL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	SubmitL(iOrigin, iDestination, iTravelOption, iCallback.Unmanage());
	}

void CHow2GoQuery::Cancel()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	if(iHow2GoTransaction != RHTTPTransaction())
		iHow2GoTransaction.Cancel();
	iHow2GoTransaction.Close();
	iHow2GoTransaction = RHTTPTransaction();
	}



void CHow2GoQuery::SubmitL(const TCoordinate& aOrigin, const TCoordinate& aDestination, TInt aTravelOption, CHow2GoQuery::TCallbackType* aCallback)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iCallback = aCallback;
	iOrigin = aOrigin;
	iDestination = aDestination;
	iTravelOption = aTravelOption;
	if(iHow2GoTransaction != RHTTPTransaction())
		{
		iHow2GoTransaction.Cancel();
		iHow2GoTransaction.Close();
		}
	iHow2GoTransaction = RHTTPSessionOpenTransactionL(iHTTPSession, _L8("http://203.125.7.41/PTWebService/webservicecall.php"), *iHow2GoTransactionTarget, HTTP::EPOST);
	TBuf8<4096> postData;
	TTime now;
	now.HomeTime();
	TBuf<128> timeHourMinutes;
	now.FormatL(timeHourMinutes, _L("%H%T"));
	//timeHourMinutes.Format(_L("1400"));
	postData.Format(_L8("params=%%3Csvctype%%3Eget_otp_route%%3C%%2Fsvctype%%3E%%3Corigin%%3E%.14f%%2C%.14f%%3C%%2Forigin%%3E%%3Cdestination%%3E%.14f%%2C%.14f%%3C%%2Fdestination%%3E%%3Cfare_type%%3E0%%3C%%2Ffare_type%%3E%%3Cpayment_mode%%3E0%%3C%%2Fpayment_mode%%3E%%3Ctravel_type%%3E0%%3C%%2Ftravel_type%%3E%%3Ctransport_mode%%3E%d%%3C%%2Ftransport_mode%%3E%%3Ctravel_day%%3E0%%3C%%2Ftravel_day%%3E%%3Ctravel_time%%3E%S%%3C%%2Ftravel_time%%3E%%3Cmax_transfer_no%%3E2%%3C%%2Fmax_transfer_no%%3E%%3Cwalking_dist%%3E600%%3C%%2Fwalking_dist%%3E"),
				aOrigin.Latitude(),
				aOrigin.Longitude(),
				aDestination.Latitude(),
				aDestination.Longitude(),
				aTravelOption,
				wideToNarrowLC(timeHourMinutes)
				);
	CleanupStack::PopAndDestroy(1); // wideToNarrowLC(timeHourMinutes)
	iHow2GoTransactionSource->SetPostDataL(postData);
	iHow2GoTransaction.Request().SetBody(*iHow2GoTransactionSource);
	RHTTPRequestSetHeaderL(iHTTPSession, iHow2GoTransaction.Request(), HTTP::EContentType, _L8("application/x-www-form-urlencoded; charset=utf-8"));
	RHTTPRequestSetHeaderL(iHTTPSession, iHow2GoTransaction.Request(), HTTP::EAcceptEncoding, _L8("gzip"));
	iHow2GoTransaction.SubmitL();
	iHow2GoTimeoutTarget->Cancel();
	iHow2GoTimeoutTimer.After(iHow2GoTimeoutTarget->iStatus, kHow2GoTimeout);
	iHow2GoTimeoutTarget->SetActive();
	}

TInt CHow2GoQuery::How2GoTransactionError(TInt aError, RHTTPTransaction aTransaction)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	//REMOVED if(aTransaction != RHTTPTransaction()) aTransaction.Cancel();
	//REMOVED LCleanedupRef<RHTTPTransaction> cleanupTransaction(aTransaction);
	
	(*iCallback.Get())(this, aError, iHow2GoStatusCode, NULL);
	
	//if(aError == KErrNotReady || aError == KErrConnectionTerminated || aError == DndTimedOut) // not connected or connection lost
	//	doc->ConnectL();
	
	return KErrNone;
	}


void CHow2GoQuery::CloseConnectionL()
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	Cancel();
	iHTTPSession.Close();
	}


void CHow2GoQuery::SetConnectionInfoL(const RConnection& aConnection, const RSocketServ& aSocketServ)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	iHTTPSession.OpenL();
	RStringPool strP = iHTTPSession.StringPool();
	RHTTPConnectionInfo connInfo = iHTTPSession.ConnectionInfo();
	connInfo.SetPropertyL ( strP.StringF(HTTP::EHttpSocketServ, RHTTPSession::GetTable() ), THTTPHdrVal (aSocketServ.Handle()) );
	TInt connPtr = REINTERPRET_CAST(TInt, &aConnection);
	connInfo.SetPropertyL ( strP.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable() ), THTTPHdrVal (connPtr) );
	_LIT8(KUserAgent, "How2Go 1.50 (iPod touch; iPhone OS 5.0.1; fr_FR)");
	_LIT8(KAccept, "*/*");
	_LIT8(KClose, "close");
	RHTTPSessionSetHeaderL(iHTTPSession, HTTP::EUserAgent, KUserAgent);
	RHTTPSessionSetHeaderL(iHTTPSession, HTTP::EConnection, KClose);
	}



void CHow2GoQuery::ReceivedHow2GoAnswer2L(RHTTPTransaction aTransaction, THTTPEvent aEvent, TInt aLastError, TPtrC8 aBuf)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	LCleanedupPtr<COutput> output ( new (ELeave) COutput );
	
	iHow2GoTimeoutTarget->Cancel();
	iHow2GoTransaction = RHTTPTransaction();
	TInt iHow2GoStatusCode = aTransaction.Response().StatusCode();
	if(aEvent.iStatus != THTTPEvent::ESucceeded)
		User::Leave(aLastError < 0 ? aLastError : KErrAbort);
	
	THTTPHdrVal contentEncodingHdrVal;
	TInt ret = aTransaction.Response().GetHeaderCollection().GetField(
					aTransaction.Session().StringPool().StringF(HTTP::EContentEncoding, RHTTPSession::GetTable()), 0,
					contentEncodingHdrVal
					);
	const TDesC8& contentEncoding( ret == KErrNone ? contentEncodingHdrVal.StrF().DesC() : KNullDesC8);
	if(ret == KErrNone && contentEncoding == _L8("gzip"))
		output->iHow2GoAnswer = iDeflateDecompressor->DecompressGZipL(aBuf).AllocL();
	else
		output->iHow2GoAnswer = aBuf.AllocL();

	{
	TPtrC8 statusReasonBuf = *output->iHow2GoAnswer;
	output->iHow2GoStatus.Set( IrisExtractField(_L8("<status>"), _L8("</status>"), statusReasonBuf) );
	output->iHow2GoReason.Set( IrisExtractField(_L8("<reason>"), _L8("</reason>"), statusReasonBuf) );
	}
	
	if(output->iHow2GoStatus == _L8("success"))
		{
		TPtrC8 buf (*output->iHow2GoAnswer);
		for(
				TPtrC8 itineraryBuf = IrisExtractField(_L8("<itinerary>"), _L8("</itinerary>"), buf);
				itineraryBuf.Length();
				itineraryBuf.Set( IrisExtractField(_L8("<itinerary>"), _L8("</itinerary>"), buf)))
			{
			COutput::CItinerary* itinerary = new (ELeave) COutput::CItinerary;
			CleanupStack::PushL(itinerary);
			
			TPtrC8 itineraryBufIt = itineraryBuf;
			TPtrC8 totalBuf = IrisExtractField(_L8("</id>"), _L8("<legs>"), itineraryBuf);
			for(
					TPtrC8 legBuf = IrisExtractField(_L8("<leg>"), _L8("</leg>"), itineraryBufIt);
					legBuf.Length();
					legBuf.Set( IrisExtractField(_L8("<leg>"), _L8("</leg>"), itineraryBufIt)))
				{
				TPtrC8 tempLegBuf = legBuf;
				TPtrC8 mode = IrisExtractField(_L8("<mode>"), _L8("</mode>"), tempLegBuf);
				if(mode != _L8("walk"))
				{
				COutput::CItinerary::TItem item = {  COutput::CItinerary::TItem::ELEG_FROM, legBuf };
				itinerary->iItems.AppendL(item);
				}
				{
				COutput::CItinerary::TItem item = {  COutput::CItinerary::TItem::ELEG_TO, legBuf };
				itinerary->iItems.AppendL(item);
				}
				}
			if(totalBuf.Length())
				{
				COutput::CItinerary::TItem item = {  COutput::CItinerary::TItem::ETOTAL, totalBuf };
				itinerary->iItems.AppendL(item);
				}
			
			output->iItineraries.AppendL(itinerary);
			CleanupStack::Pop(itinerary);
			}
		}
	
	(*iCallback2.Get())(this, aLastError, iHow2GoStatusCode, output.Unmanage());
	
	aTransaction.Cancel();
	aTransaction.Close(); // note: if a panic is raised before reaching this line, MHFRunError will close the transaction
	}


void CHow2GoQuery::ReceivedHow2GoAnswerL(RHTTPTransaction aTransaction, THTTPEvent aEvent, TInt aLastError, TPtrC8 aBuf)
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	CSingaporeBusDocument* doc = (CSingaporeBusDocument*)CEikonEnv::Static()->EikAppUi()->Document();
	LCleanedupPtr<CJourneyPlannerOutput> output ( new (ELeave) CJourneyPlannerOutput );
	
	iHow2GoTimeoutTarget->Cancel();
	iHow2GoTransaction = RHTTPTransaction();
	TInt iHow2GoStatusCode = aTransaction.Response().StatusCode();
	if(aEvent.iStatus != THTTPEvent::ESucceeded)
		User::Leave(aLastError < 0 ? aLastError : KErrAbort);
	
	THTTPHdrVal contentEncodingHdrVal;
	TInt ret = aTransaction.Response().GetHeaderCollection().GetField(
					aTransaction.Session().StringPool().StringF(HTTP::EContentEncoding, RHTTPSession::GetTable()), 0,
					contentEncodingHdrVal
					);
	const TDesC8& contentEncoding( ret == KErrNone ? contentEncodingHdrVal.StrF().DesC() : KNullDesC8);
	TPtrC8 how2GoAnswer = aBuf;
	if(ret == KErrNone && contentEncoding == _L8("gzip"))
		how2GoAnswer.Set( iDeflateDecompressor->DecompressGZipL(aBuf) );

	{
	TPtrC8 statusReasonBuf = how2GoAnswer;
	TPtrC8 status = IrisExtractField(_L8("<status>"), _L8("</status>"), statusReasonBuf);
	output->iStatus = (status == _L8("success")) ? CJourneyPlannerOutput::ESuccess : CJourneyPlannerOutput::EError;
	output->iErrorMessage = narrowToWideL( IrisExtractField(_L8("<reason>"), _L8("</reason>"), statusReasonBuf) );
	}
	
	/*{
	RFile file;
	file.Replace(doc->iFsData, _L("how2Query.xml"), EFileWrite);
	file.Write(how2GoAnswer);
	file.Close();
	}*/
	
	if(output->iStatus == CJourneyPlannerOutput::ESuccess)
		{
		TPtrC8 buf (how2GoAnswer);
		for(
				TPtrC8 itineraryBuf = IrisExtractField(_L8("<itinerary>"), _L8("</itinerary>"), buf);
				itineraryBuf.Length();
				itineraryBuf.Set( IrisExtractField(_L8("<itinerary>"), _L8("</itinerary>"), buf)))
			{
			CJourneyPlannerOutput::CItinerary* itinerary = new (ELeave) CJourneyPlannerOutput::CItinerary;
			CleanupStack::PushL(itinerary);
			
			TPtrC8 itineraryBufIt = itineraryBuf;
			TPtrC8 totalBuf = IrisExtractField(_L8("</id>"), _L8("<legs>"), itineraryBuf);

			{
			TPtrC8 tempTotalBuf = totalBuf;
			TPtrC8 duration = IrisExtractField(_L8("<duration>"), _L8("</duration>"), tempTotalBuf);
			TPtrC8 waitingAmount = IrisExtractField(_L8("<waitingAmount>"), _L8("</waitingAmount>"), tempTotalBuf);
			TPtrC8 totalDistance = IrisExtractField(_L8("<totalDistance>"), _L8("</totalDistance>"), tempTotalBuf);
			{
			TReal32 duration_h;
			TLex8 lex(duration); lex.Val(duration_h);
			itinerary->iDuration = duration_h*60*60;
			}
			{
			TReal32 totalDistance_km; 
			TLex8 lex(totalDistance); lex.Val(totalDistance_km);
			itinerary->iDistance = totalDistance_km*1000;
			}
			}
			
			TInt itineraryFare_cents = 0;
			
			
			for(
					TPtrC8 legBuf = IrisExtractField(_L8("<leg>"), _L8("</leg>"), itineraryBufIt);
					legBuf.Length();
					legBuf.Set( IrisExtractField(_L8("<leg>"), _L8("</leg>"), itineraryBufIt)))
				{
				TPtrC8 tempLegBuf = legBuf;
				TPtrC8 service = IrisExtractField(_L8("<service>"), _L8("</service>"), tempLegBuf);
				TPtrC8 mode = IrisExtractField(_L8("<mode>"), _L8("</mode>"), tempLegBuf);
				
				TPtrC8 leg_duration = IrisExtractField(_L8("<leg_duration>"), _L8("</leg_duration>"), tempLegBuf);
				TPtrC8 leg_distance = IrisExtractField(_L8("<leg_distance>"), _L8("</leg_distance>"), tempLegBuf);
				TPtrC8 no_of_stops = IrisExtractField(_L8("<no_of_stops>"), _L8("</no_of_stops>"), tempLegBuf);
				TPtrC8 route = IrisExtractField(_L8("<route>"), _L8("</route>"), tempLegBuf);

				TPtrC8 from_leg = IrisExtractField(_L8("<from_leg>"), _L8("</from_leg>"), tempLegBuf);
				TPtrC8 from_stop_id = IrisExtractField(_L8("<stop_id>"), _L8("</stop_id>"), from_leg);
				TPtrC8 from_leg_name = IrisExtractField(_L8("<leg_name>"), _L8("</leg_name>"), from_leg);
				TPtrC8 from_leg_name_buf = from_leg_name;
				TPtrC8 from_station_number = IrisExtractField(_L8("("), _L8(")"), from_leg_name_buf);
				TPtrC8 from_type = IrisExtractField(_L8("<type>"), _L8("</type>"), from_leg);
				
				TPtrC8 to_leg = IrisExtractField(_L8("<to_leg>"), _L8("</to_leg>"), tempLegBuf);
				TPtrC8 to_stop_id = IrisExtractField(_L8("<stop_id>"), _L8("</stop_id>"), to_leg);
				TPtrC8 to_leg_name = IrisExtractField(_L8("<leg_name>"), _L8("</leg_name>"), to_leg);
				TPtrC8 to_leg_name_buf = to_leg_name;
				TPtrC8 to_station_number = IrisExtractField(_L8("("), _L8(")"), to_leg_name_buf);
				TPtrC8 to_type = IrisExtractField(_L8("<type>"), _L8("</type>"), to_leg);

				TPtrC8 leg_fare = IrisExtractField(_L8("<leg_fare>"), _L8("</leg_fare>"), tempLegBuf);
				

				CJourneyPlannerOutput::CItinerary::CLeg* leg = new (ELeave) CJourneyPlannerOutput::CItinerary::CLeg;
				CleanupStack::PushL(leg);
				
				if(mode == _L8("walk"))
					leg->iMode = CJourneyPlannerOutput::CItinerary::CLeg::WALK;
				else if(mode == _L8("bus"))
					leg->iMode = CJourneyPlannerOutput::CItinerary::CLeg::BUS;
				else
					leg->iMode = CJourneyPlannerOutput::CItinerary::CLeg::OTHER;

				{
				TReal32 leg_duration_h;
				TLex8 lex(leg_duration); lex.Val(leg_duration_h);
				leg->iDuration = leg_duration_h*60*60;
				}
				{ TLex8 lex(no_of_stops); lex.Val(leg->iCountStops); }
				{
				TReal32 leg_distance_km;
				TLex8 lex(leg_distance); lex.Val(leg_distance_km);
				leg->iDistance = leg_distance_km*1000;
				}
				{
				TInt leg_fare_cents;
				TLex8 lex(leg_fare); lex.Val(leg_fare_cents);
				itineraryFare_cents += leg_fare_cents;
				TBuf<128> legFare;
				legFare.Format(_L("%.2f S$"), TReal(leg_fare_cents)/100);
				leg->iLegFare = legFare.AllocL();
				}
				{
				if(from_leg_name.Length()) leg->iFromLocation = narrowToWideL(from_leg_name);
				if(to_leg_name.Length()) leg->iToLocation = narrowToWideL(to_leg_name);
				}
				{
				if(service != _L8("-"))
					leg->iServiceDesc = narrowToWideL(service);
				else if(route != _L8("-"))
					leg->iServiceDesc = narrowToWideL(route);
				else if(mode != _L8("-"))
					leg->iServiceDesc = narrowToWideL(mode);
				}
				
				
				{
				TStation target = { from_station_number };
				TInt ret = doc->iStationsSortedByStationNumber.SpecificFindInOrder(&target, TLinearOrder<const TStation*>(&CSingaporeBusDocument::TLinearOrder_Station), EArrayFindMode_Any);
				leg->iFromStation = (ret != KErrNotFound) ? doc->iStationsSortedByStationNumber[ret] : NULL;
				}
				{
				TStation target = { to_station_number };
				TInt ret = doc->iStationsSortedByStationNumber.SpecificFindInOrder(&target, TLinearOrder<const TStation*>(&CSingaporeBusDocument::TLinearOrder_Station), EArrayFindMode_Any);
				leg->iToStation = (ret != KErrNotFound) ? doc->iStationsSortedByStationNumber[ret] : NULL;
				}
				
				if(leg->iFromStation)
				{
				TPtrC8 singleService = service;
				// below 2 lines commented because if we extract one service, then we can't see the others in CLondonBusServiceRouteView
				// if(service.Locate('/') != KErrNotFound) // if service = 'XXX/YYY/ZZZ', then we take the first service
				//	singleService.Set( service.Left(service.Locate('/')) );
				TInt i;
				for(i = leg->iFromStation->iServicesBeg ;
					(i < leg->iFromStation->iServicesEnd) &&
					(doc->iStationServiceKeys[i].Left( doc->iStationServiceKeys[i].Locate(' ')).CompareF(singleService) != 0 ); //find the service key for our service name
					++i);
				const TService* service = NULL;
				if(i < leg->iFromStation->iServicesEnd)
					{
					TBuf8<15> serviceKeyUpper( doc->iStationServiceKeys[i] );
					serviceKeyUpper.UpperCase();
					TService target = { serviceKeyUpper };
					TInt ret = doc->iServicesSortedByServiceKey.SpecificFindInOrder(&target, TLinearOrder<const TService*>(&CSingaporeBusDocument::TLinearOrder_Service), EArrayFindMode_Any);
					leg->iService = (ret != KErrNotFound) ? doc->iServicesSortedByServiceKey[ret] : NULL;
					}
				}
								
				itinerary->iLegs.AppendL(leg);
				CleanupStack::Pop(leg);
				}
			
			if(itineraryFare_cents > 0)
			{
			TBuf<128> itineraryFare;
			itineraryFare.Format(_L("%.2f S$"), TReal(itineraryFare_cents)/100);
			itinerary->iFare = itineraryFare.AllocL();
			}
			
			output->iItineraries.AppendL(itinerary);
			CleanupStack::Pop(itinerary);
			}
		}
	
	(*iCallback.Get())(this, aLastError, iHow2GoStatusCode, output.Unmanage());
	
	aTransaction.Cancel();
	aTransaction.Close(); // note: if a panic is raised before reaching this line, MHFRunError will close the transaction
	}


TBool CHow2GoQuery::Pending() const
	{
	TFunTrace funTrace(__PRETTY_FUNCTION__);
	return (iHow2GoTransaction != RHTTPTransaction());
	}

